/* $RCSFile: EntityConfig.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/CRMWrapper3/EntityConfig.cs $
 * $log$
 * Revision 19 2010/10/02 14:03:22  christian.surieux
 *   Corrected a bug in Crm2Crm concerning RetrieveLookup
 * Revision 6 2010/08/06 19:56:32  christian.surieux
 *   Added Log to All cs files
 */

// =======================================================================
//  File: EntityConfig.cs
// =======================================================================
//  This file is part of Addon Nice product.
//  Copyright (C) Addon Nice.  All rights reserved.
// =======================================================================

using System;
using System.Diagnostics;
using System.Collections.Specialized;
using System.Text;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Resources;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;
using CrmSdk;
using System.Collections.Generic;
using AddonNice.Settings;

namespace AddonNice.CRMWrapper
{
    public delegate void InsertDicDelegate(string k,InternalAttributeMetadata val);
    public delegate void InsertDic2Delegate(string k,InternalRelationMetadata val);
    public delegate void InsertList2Delegate(InternalRelationMetadata val);
    
    /// <summary>
    /// This class allows to get/set attributes on a crm entity.
    /// </summary>
    public class EntityConfig
    {

        protected SortedList<string,InternalAttributeMetadata> _attributesDic       = new SortedList<string,InternalAttributeMetadata>();
        protected List<InternalRelationMetadata> _One2ManyRelationList              = new List<InternalRelationMetadata>();
        protected List<InternalRelationMetadata> _Many2OneRelationList               = new List<InternalRelationMetadata>();
        protected List<InternalRelationMetadata> _Many2ManyRelationList             = new List<InternalRelationMetadata>();

        /// <summary>
        /// SortedList of internalproperties: keys are schema names and values are InternalAttributeMetadata objects, 
        /// to be casted to correct type according attribute
        /// </summary>
        public SortedList<string,InternalAttributeMetadata> attributesDic
        {
            get
            {
                return _attributesDic;
            }
        }

        List<InternalRelationMetadata> One2ManyRelationList
        {
            get
            {
                return _One2ManyRelationList;
            }
        }

        List<InternalRelationMetadata> Many2OneRelationList
        {
            get
            {
                return _Many2OneRelationList;
            }
        }

        List<InternalRelationMetadata> Many2ManyRelationList
        {
            get
            {
                return _Many2ManyRelationList;
            }
        }
        
        private string _entityName;

        /// <summary>
        /// This is the schema name for the current entity
        /// Specified in constructor or default value
        /// </summary>
        public string entityName
        {
            get
            {
                return _entityName;
            }
        }

        private bool _isAdmin = false;
        /// <summary>
        /// Access will use theCRM admi account if set to true, else will use current user account
        /// Specified in constructor or default value
        /// default value 		false
        /// </summary>
        public bool isAdmin
        {
            get
            {
                return _isAdmin;
            }
        }

        private CRMWrapperBase _wrapper = null;
        /// <summary>
        /// Wrapper needed in each constructor
        /// </summary>
        protected CRMWrapperBase wrapper
        {
            get
            {
                return _wrapper;
            }
        }

        EntityInternalItems _entityFlags = EntityInternalItems.All;
        /// <summary>
        /// This specify the entityFlag to be used to get attributes from metadata, specified in constructor or default value
        /// default value 		EntityFlags.All
        /// </summary>
        public EntityInternalItems entityFlags
        {
            get
            {
                return _entityFlags;
            }
        }

        public bool includeAttributes
        {
            get
            {
                return  ( entityFlags == EntityInternalItems.All ) || ( (entityFlags & EntityInternalItems.IncludeAttributes) == EntityInternalItems.IncludeAttributes );
            }
        }

        public bool includeRelations
        {
            get
            {
                return  ( entityFlags == EntityInternalItems.All ) || ( (entityFlags & EntityInternalItems.IncludeRelationships) == EntityInternalItems.IncludeRelationships );
            }
        }

        public bool includePrivileges
        {
            get
            {
                return  ( entityFlags == EntityInternalItems.All ) || ( (entityFlags & EntityInternalItems.IncludePrivileges) == EntityInternalItems.IncludePrivileges );
            }
        }

        
        AttributeInternalRequiredLevel _requiredLevel = AttributeInternalRequiredLevel.None | AttributeInternalRequiredLevel.ReadOnly | 
            AttributeInternalRequiredLevel.Recommended | AttributeInternalRequiredLevel.Required | AttributeInternalRequiredLevel.SystemRequired;
        /// <summary>
        /// This specify the required level for fields get from metadata, works as a mask, will be 'xored' with AttributeMetadata.RequiredLevel
        /// Specified in constructor or default value
        /// default value 		(int)AttributeRequiredLevel.None | (int)AttributeRequiredLevel.ReadOnly | (int)AttributeRequiredLevel.Recommended | (int)AttributeRequiredLevel.Required | (int)AttributeRequiredLevel.SystemRequired;
        /// </summary>
        public AttributeInternalRequiredLevel requiredLevel
        {
            get
            {
                return _requiredLevel;
            }
        }
        bool _ValidForRead = true;
        public bool ValidForRead
        {
            get
            {
                return _ValidForRead;
            }
        }


        InternalDisplayMasks _DisplayMask = (InternalDisplayMasks.None | InternalDisplayMasks.PrimaryName | 
                        InternalDisplayMasks.ValidForForm | InternalDisplayMasks.ValidForGrid | 
                        InternalDisplayMasks.ValidForAdvancedFind | InternalDisplayMasks.RequiredForForm | 
                        InternalDisplayMasks.RequiredForGrid );
        public InternalDisplayMasks DisplayMask
        {
            get
            {
                return _DisplayMask;
            }
        }

        /// <summary>
        /// This is the EntityMetaData avoid to use directly, temporary public should get protected
        /// </summary>
        EntityInternalMetadata _entityMetaData  =   null;
        public EntityInternalMetadata entityMetaData
        {
            get
            {
                if ( _entityMetaData == null )
                    _entityMetaData =   MetadataCache.GetEntityMetadata(wrapper, _entityName, entityFlags, isAdmin);
                return _entityMetaData;
            }
        }

        
        /// <summary>
        /// This is the EntityMetaData.ObjectTypeCode
        /// </summary>
        public int ObjectTypeCode
        {
            get
            {
                int otpc    =   wrapper.Util.ExtractObjectTypeCode(entityMetaData);
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig ObjectTypeCode GET entityName: {0}, ObjectTypeCode: {1}", entityName, otpc),CRMQueryTraceSwitch.Sw.Info);
                return otpc;
            }
        }

        /// <summary>
        /// This is the EntityMetaData.DisplayName
        /// </summary>
        public string DisplayName
        {
            get
            {
                return wrapper.Util.ExtractDisplayName(entityMetaData);
            }
        }

        public string PrimaryKey
        {
            get
            {
                return wrapper.Util.ExtractPrimaryKeyName(entityMetaData);
            }
        }

        public string PrimaryField
        {
            get
            {
                return wrapper.Util.ExtractPrimaryFieldName(entityMetaData);
            }
        }

        /// <summary>
        /// This constructor will trigger the request to fill entity attributes according transmitted flag
        /// only attributes that comply with the transmitted required level will be allowed 
        /// </summary>
        public EntityConfig(CRMWrapperBase w, string entName, EntityInternalItems f,AttributeInternalRequiredLevel reqLev, bool isAdm, InternalDisplayMasks displayMask, bool validForRead)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( 
                    string.Format("[V]EntityConfig Constructor Entity:{0}, isAdmin: {1}, Flags: {2}, requiredLevel: {3}, DisplayMask: [{4}], ValidForRead: [{5}]", 
                    new Object[] { entName, isAdm,f,reqLev, displayMask, validForRead }),CRMQueryTraceSwitch.Sw.Info);
            _isAdmin        =   isAdm;
            _wrapper        =   w;
            _entityName     =   entName;
            _entityFlags    =   f;
            _requiredLevel  =   reqLev;
            _DisplayMask    =   displayMask;
            _ValidForRead   =   validForRead;
            GetAttributeFields();
            GetRelations();
        }

        /// <summary>
        /// This constructor will trigger the request to fill entity attributes according transmitted flag
        /// only attributes that comply with the transmitted required level will be allowed 
        /// </summary>
        public EntityConfig(CRMWrapperBase w, string entName, EntityInternalItems f, AttributeInternalRequiredLevel reqLev, bool isAdm, InternalDisplayMasks displayMask)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( 
                string.Format("[V]EntityConfig Constructor Entity:{0}, isAdmin: {1}, Flags: {2}, requiredLevel: {3}, DisplayMask: [{4}]", 
                new Object[] { entName, isAdm,f,reqLev, displayMask}),CRMQueryTraceSwitch.Sw.Info);
             _isAdmin       =   isAdm;
            _wrapper        =   w;
            _entityName     =   entName;
            _entityFlags    =   f;
            _requiredLevel  =   reqLev;
            _DisplayMask    =   displayMask;

            GetAttributeFields();
            GetRelations();
        }

        /// <summary>
        /// This constructor will trigger the request to fill entity attributes according transmitted flag
        /// only attributes that comply with the transmitted required level will be allowed 
        /// </summary>
        public EntityConfig(CRMWrapperBase w, string entName, EntityInternalItems f,AttributeInternalRequiredLevel reqLev, bool isAdm)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( 
                    string.Format("[V]EntityConfig Constructor Entity:{0}, isAdmin: {1}, Flags: {2}, requiredLevel: {3}", 
                    new Object[] { entName, isAdm,f,reqLev}),CRMQueryTraceSwitch.Sw.Info);
            _isAdmin        =   isAdm;
            _wrapper        =   w;
            _entityName     =   entName;
            _entityFlags    =   f;
            _requiredLevel  =   reqLev;
            GetAttributeFields();
            GetRelations();
        }

        /// <summary>
        /// This constructor will trigger the request to fill entity attributes according transmitted flag
        /// </summary>
        public EntityConfig(CRMWrapperBase w, string entName, EntityInternalItems f, bool isAdm)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( 
                    string.Format("[V]EntityConfig Constructor Entity:{0}, isAdmin: {1}, Flags: {2}", entName, isAdm,f),CRMQueryTraceSwitch.Sw.Info);
            _isAdmin        =   isAdm;
            _wrapper        =   w;
            _entityName     =   entName;
            _entityFlags    =   f;
            GetAttributeFields();
            GetRelations();
        }

        /// <summary>
        /// Version where flags default to EntityFlags.All 
        /// </summary>
        public EntityConfig(CRMWrapperBase w, string entName, bool isAdm)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( 
                    string.Format("[V]EntityConfig Constructor Entity:{0}, isAdmin: {1}", entName, isAdm),CRMQueryTraceSwitch.Sw.Info);
            _isAdmin        =   isAdm;
            _wrapper        =   w;
            _entityName     =   entName;
            _entityFlags    =   EntityInternalItems.All;
            GetAttributeFields();
            GetRelations();
        }

        /// <summary>
        /// Version where flags default to EntityFlags.All and isAdmin is true
        /// </summary>
        public EntityConfig(CRMWrapperBase w, string entName)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( 
                    string.Format("[V]EntityConfig Constructor Entity:{0}", entName),CRMQueryTraceSwitch.Sw.Info);
            _isAdmin        =   true;
            _wrapper        =   w;
            _entityName     =   entName;
            _entityFlags    =   EntityInternalItems.All;
            GetAttributeFields();
            GetRelations();
        }
        
        /// <summary>
        /// Retrieves from metadata all available and selected fields according Flags, requiredLevel mask and 
        /// using the admin or user account according what has been set in constructor
        /// </summary>
        private void GetAttributeFields()
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig GetAttributeFields Entity:{0}, parameter displayMask: [{1}]", entityName, DisplayMask),CRMQueryTraceSwitch.Sw.Info);

            if ( !includeAttributes )
                return;
            
            string entityConfigKey  = string.Format("CONFIG_ATT_KEY_{0}_{1}_{2}_{3}_{4}_{5}_{6}", 
                new object[] { entityName, entityFlags, requiredLevel,DisplayMask, isAdmin,wrapper.OrganizationName,wrapper.CrmVersion });
            entityConfigKey         =   entityConfigKey.Replace(' ','_').Replace(',','_');
                
            // Retrieve entity metadata from cache
            SortedList<string,InternalAttributeMetadata> hstb = (SortedList<string,InternalAttributeMetadata>)Settings.Cache.CurrentCache.Get(entityConfigKey);
            if (hstb != null)
            {
                _attributesDic      =   hstb;
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig GetAttributeFields match key: {0}", entityConfigKey),CRMQueryTraceSwitch.Sw.Info);
                return;
            }
            // Iterate through all the entity's attributes
            attributesDic.Clear();
            wrapper.Util.IterateAttributs(entityName,entityMetaData,attributesDic,
                delegate(string k,InternalAttributeMetadata val){attributesDic.Add(k,val);});
            // Insert the entity metadata into the cache 60 minutes
            Settings.Cache.CurrentCache.InsertMinutes(entityConfigKey, attributesDic,Config.CRMMetadataCacheTime);
        }

        private void GetRelations()
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]EntityConfig GetRelations BEG",CRMQueryTraceSwitch.Sw.Info);
            
            if ( !includeRelations )
                return;

            string entityConfigKey          =   string.Format("CONFIG_REL_KEY_{0}_{1}_{2}_{3}_{4}", 
                new object[] { entityName, entityFlags,isAdmin,wrapper.OrganizationName,wrapper.CrmVersion });
            entityConfigKey                 =   entityConfigKey.Replace(' ','_').Replace(',','_');
                
            // Retrieve entity metadata from cache
            object[] savRel                 =   (object[])Settings.Cache.CurrentCache.Get(entityConfigKey);
            if ( savRel != null )
            {
                _One2ManyRelationList       =   (List<InternalRelationMetadata>)savRel[0];
                _Many2OneRelationList       =   (List<InternalRelationMetadata>)savRel[1];
                _Many2ManyRelationList      =   (List<InternalRelationMetadata>)savRel[2];
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig GetRelations match key: {0}", entityConfigKey),CRMQueryTraceSwitch.Sw.Info);
                return;
            }
            One2ManyRelationList.Clear();
            wrapper.Util.IterateOne2ManyRelations(entityName,entityMetaData,One2ManyRelationList,
                delegate(InternalRelationMetadata val){One2ManyRelationList.Add(val);});
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig GetRelations One2ManyRelationDic Count: {0}", One2ManyRelationList.Count),CRMQueryTraceSwitch.Sw.Info);
            Many2OneRelationList.Clear();
            wrapper.Util.IterateMany2OneRelations(entityName,entityMetaData,Many2OneRelationList,
                delegate(InternalRelationMetadata val){Many2OneRelationList.Add(val);});
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig GetRelations Many2OneRelationList Count: {0}", Many2OneRelationList.Count),CRMQueryTraceSwitch.Sw.Info);
            Many2ManyRelationList.Clear();
            wrapper.Util.IterateMany2ManyRelations(entityName,entityMetaData,Many2ManyRelationList,
                delegate(InternalRelationMetadata val){Many2ManyRelationList.Add(val);});
            savRel                          =   new object[]{One2ManyRelationList,Many2OneRelationList,Many2ManyRelationList};

            // Insert the entity metadata into the cache 10 heures
            Settings.Cache.CurrentCache.InsertMinutes(entityConfigKey, savRel, Config.CRMMetadataCacheTime);
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig GetRelations END Many2ManyRelationDic Count: {0}", Many2ManyRelationList.Count),CRMQueryTraceSwitch.Sw.Info);
        }

        /// <summary>
        /// Get the AttributeMetadata from its schema name
        /// </summary>
        public InternalAttributeMetadata this[string key]
        {
            get
            {
                if (attributesDic.ContainsKey(key))
                    return attributesDic[key];
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig this[{0}] not found", key),CRMQueryTraceSwitch.Sw.Info);
                return null;
            }
            set
            {
                attributesDic[key] = value;
            }
        }

        /// <summary>
        /// Get the AttributeMetadata from its index in the metadata collection
        /// </summary>
        public InternalAttributeMetadata this[int i]
        {
            get
            {
                return wrapper.Util.ExtractAttribute(entityMetaData,i);
            }
        }

        public int AttributeCount
        {
            get
            {
                return wrapper.Util.ExtractAttributeCount(entityMetaData);
            }
        }

        public InternalRelationMetadata One2ManyRelation(string entity,string attribute)
        {
            InternalRelationMetadata  rel   =    wrapper.Util.GetOne2ManyRelation(entity,entityMetaData,attribute);
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose && rel == null )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig One2ManyRelation not found entity:{0}, attribute: {1}",entity, attribute),CRMQueryTraceSwitch.Sw.Info);
            return rel;
        }

        public InternalRelationMetadata Many2OneRelation(string attribute)
        {
            InternalRelationMetadata  rel   =    wrapper.Util.GetMany2OneRelation(entityName,entityMetaData,attribute,false);
            if ( rel == null )
                rel                         =    wrapper.Util.GetMany2OneRelation(entityName,entityMetaData,attribute,true);
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose && rel == null )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig Many2OneRelation not found entity:{0}, attribute: {1}",entityName, attribute),CRMQueryTraceSwitch.Sw.Info);
            return rel;
        }

        public int Many2OneRelationCount(string attribute)
        {
            int cnt     =   wrapper.Util.GetMany2OneRelationCount(entityName,entityMetaData,attribute);
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig Many2OneRelation not found entity:{0}, attribute: {1}",entityName, attribute),CRMQueryTraceSwitch.Sw.Info);
            return cnt;
        }

        public InternalRelationMetadata Many2OneSelfRelation(string attribute)
        {
            InternalRelationMetadata  rel   =    wrapper.Util.GetMany2OneRelation(entityName,entityMetaData,attribute,true);
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose && rel == null )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig Many2OneRelation not found entity:{0}, attribute: {1}, self: {2}",entityName, attribute),CRMQueryTraceSwitch.Sw.Info);
            return rel;
        }

        public InternalRelationMetadata Many2ManyRelation(string entity,string attribute)
        {
            InternalRelationMetadata  rel   =    wrapper.Util.GetMany2ManyRelation(entity,entityMetaData,attribute);
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose && rel == null )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig Many2ManyRelation not found entity:{0}, attribute: {1}",entity, attribute),CRMQueryTraceSwitch.Sw.Info);
            return rel;
        }

        public List<string> FindPotentialLookupEntities(string lookupname)
        {
            List<string> relLst     =   new List<string>();
            foreach (InternalRelationMetadata rel in Many2OneRelationList)
            {
                string relEntity    =   wrapper.Util.ExtractLookupEntityFromMany2OneRelation(entityName,rel,lookupname);
                if ( relEntity != string.Empty )
                    relLst.Add(relEntity);
            }
            return relLst;
        }

        public string FindLookupEntity(string lookupname)
        {
            return wrapper.Util.ExtractRelationFieldAsString(Many2OneRelation(lookupname), InternalEntityRelationshipType.OneToMany, InternalRelationField.ReferencedEntity);
        }

        public string FindSelfLookupEntity(string lookupname)
        {
            return wrapper.Util.ExtractRelationFieldAsString(Many2OneSelfRelation(lookupname), InternalEntityRelationshipType.OneToMany, InternalRelationField.ReferencedEntity);
        }
        /// <summary>
        /// Builds up HTML to be used as list table
        /// </summary>
        /// <param name="fieldList">Arraylist being used to render table</param>
        /// <returns>HTML of table</returns>
        private string BuildhtmlTable()
        {
            StringBuilder htmlBuilder = new StringBuilder();

            htmlBuilder.Append("<table cellSpacing=\"0\" cellPadding=\"2\" width=\"100%\">");
            int cnt =   AttributeCount;
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig BuildhtmlTable AttributeCount :{0}", cnt),CRMQueryTraceSwitch.Sw.Info);

            // Add every field in arraylist as a new row
            for (int i = 0; i < cnt; i++)
            {
                htmlBuilder.AppendFormat("<tr name=\"{0}\" label=\"{1}\" req=\"{2}\" selected=\"0\" onclick=\"selectRow(true);\" ondblclick=\"MoveToOtherList();\" onkeydown=\"HandleKeyDown()\">", 
                    AttFieldAsString(i,InternalAttributeFields.Name), 
                    AttFieldAsString(i,InternalAttributeFields.DisplayName), 
                    AttFieldAsInt(i,InternalAttributeFields.RequiredLevel));
                htmlBuilder.AppendFormat("<td class=\"sel\" noWrap>{0}</td></tr>", AttFieldAsString(i,InternalAttributeFields.DisplayName));
            }

            htmlBuilder.Append("</table>");

            return htmlBuilder.ToString();
        }

        public string AttFieldAsString(int AttId,InternalAttributeFields fieldName)
        {
            return wrapper.Util.ExtractAttributeFieldAsString(this[AttId],fieldName);
        }
        public int AttFieldAsInt(int AttId,InternalAttributeFields fieldName)
        {
            return wrapper.Util.ExtractAttributeFieldAsInt(this[AttId],fieldName);
        }
        public bool AttFieldAsBool(int AttId,InternalAttributeFields fieldName)
        {
            return wrapper.Util.ExtractAttributeFieldAsBool(this[AttId],fieldName);
        }

        public string AttFieldAsString(string key,InternalAttributeFields fieldName)
        {
            return wrapper.Util.ExtractAttributeFieldAsString(this[key],fieldName);
        }
        public int AttFieldAsInt(string key,InternalAttributeFields fieldName)
        {
            return wrapper.Util.ExtractAttributeFieldAsInt(this[key],fieldName);
        }
        public bool AttFieldAsBool(string key,InternalAttributeFields fieldName)
        {
            return wrapper.Util.ExtractAttributeFieldAsBool(this[key],fieldName);
        }

        
        public string One2ManyFieldAsString(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsString(One2ManyRelation(entityName,key),InternalEntityRelationshipType.OneToMany,fieldName);
        }
        public int One2ManyFieldAsInt(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsInt(One2ManyRelation(entityName,key),InternalEntityRelationshipType.OneToMany,fieldName);
        }
        public bool One2ManyFieldAsBool(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsBool(One2ManyRelation(entityName,key),InternalEntityRelationshipType.OneToMany,fieldName);
        }

        public string Many2OneFieldAsString(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsString(Many2OneRelation(key),InternalEntityRelationshipType.OneToMany,fieldName);
        }
        public int Many2OneFieldAsInt(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsInt(Many2OneRelation(key),InternalEntityRelationshipType.OneToMany,fieldName);
        }
        public bool Many2OneFieldAsBool(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsBool(Many2OneRelation(key),InternalEntityRelationshipType.OneToMany,fieldName);
        }

        public string Many2ManyFieldAsString(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsString(Many2ManyRelation(entityName,key),InternalEntityRelationshipType.ManyToMany,fieldName);
        }
        public int Many2ManyFieldAsInt(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsInt(Many2ManyRelation(entityName,key),InternalEntityRelationshipType.ManyToMany,fieldName);
        }
        public bool Many2ManyFieldAsBool(string key,InternalRelationField fieldName)
        {
            return wrapper.Util.ExtractRelationFieldAsBool(Many2ManyRelation(entityName,key),InternalEntityRelationshipType.ManyToMany,fieldName);
        }

        /// <summary>
        /// Create a new DataSet for Entities/Attributes with relation on typecode
        /// </summary>
        /// <param name="fieldList">Arraylist being used to render table</param>
        /// <returns>DataSet</returns>
        public static DataSet CreateDataSet()
        {
            DataSet ds = new DataSet();
            DataTable tb1 = CreateEntitiesTable();
            ds.Tables.Add(tb1);
            DataTable tb2 = CreateAttributesTable();
            ds.Tables.Add(tb2);
            DataColumn c1 = tb1.Columns[0];
            DataColumn c2 = tb2.Columns[0];
            ds.Relations.Add(c1, c2);
            return ds;
        }


        /// <summary>
        /// Create a new DataTable for Entities with primary key on typecode
        /// </summary>
        /// <param name="fieldList">Arraylist being used to render table</param>
        /// <returns>DataTable</returns>
        public static DataTable CreateEntitiesTable()
        {
            DataTable tb    =   new DataTable("Entities");
            DataColumn c0   =   tb.Columns.Add(CrmStr.strConfTypeCode, typeof(int));
            DataColumn c1   =   tb.Columns.Add(CrmStr.strConfSchemaName, typeof(string));
            DataColumn c2   =   tb.Columns.Add(CrmStr.strConfDisplayName, typeof(string));
            tb.PrimaryKey   =   new DataColumn[] { c0 };
            return tb;
        }

        /// <summary>
        /// Create a new DataTable for attributes with primary key on entityTypecode/schemaName
        /// </summary>
        /// <param name="fieldList">Arraylist being used to render table</param>
        /// <returns>HTML of table</returns>
        public static DataTable CreateAttributesTable()
        {
            DataTable tb    =   new DataTable("Attributes");
            DataColumn c0   =   tb.Columns.Add(CrmStr.strConfTypeCode, typeof(int));
            DataColumn c1   =   tb.Columns.Add(CrmStr.strConfSchemaName, typeof(string));
            DataColumn c2   =   tb.Columns.Add(CrmStr.strConfDisplayName, typeof(string));
            DataColumn c3   =   tb.Columns.Add(CrmStr.strConfDisplayMask, typeof(int));
            DataColumn c4   =   tb.Columns.Add(CrmStr.strConfLabelName, typeof(string));
            DataColumn c5   =   tb.Columns.Add(CrmStr.strConfRequiredLevel, typeof(int));
            DataColumn c6   =   tb.Columns.Add(CrmStr.strConfType, typeof(string));
            DataColumn c6b  =   tb.Columns.Add(CrmStr.strConfIntType, typeof(int));
            DataColumn c7   =   tb.Columns.Add(CrmStr.strConfDescription, typeof(string));
            DataColumn c8   =   tb.Columns.Add(CrmStr.strConfIsCustomField, typeof(bool));
            DataColumn c9   =   tb.Columns.Add(CrmStr.strConfDefaultValue, typeof(string)); // this will be the string value of the oject
            DataColumn c10  =   tb.Columns.Add(CrmStr.strConfValidForCreate, typeof(bool));
            DataColumn c11  =   tb.Columns.Add(CrmStr.strConfValidForRead, typeof(bool));
            DataColumn c12  =   tb.Columns.Add(CrmStr.strConfValidForUpdate, typeof(bool));
            DataColumn c13  =   tb.Columns.Add(CrmStr.strConfAttributeOf, typeof(string));

            tb.PrimaryKey   =   new DataColumn[] { c0, c1 };
            return tb;
        }

        /// <summary>
        /// Insert the entity record in a DataTable nammed "Entities"
        /// and attributes records in a DataTable nammed "Attributes"
        /// </summary>
        /// <param name="fieldList">Arraylist being used to render table</param>
        /// <returns>HTML of table</returns>
        public DataSet InsertDataSet(DataSet ds,bool includeAttributeOf,bool filterNullDisplayName, bool filterValidForRead,bool filterValidForCreate,bool filterValidForUpdate)
        {
            DataTable tb1           =   ds.Tables["Entities"];
            DataTable tb2           =   ds.Tables["Attributes"];

            DataRow rw              =   tb1.NewRow();
            rw[CrmStr.strConfTypeCode]     =   ObjectTypeCode;
            rw[CrmStr.strConfSchemaName]   =   entityName;
            rw[CrmStr.strConfDisplayName]  =   DisplayName;
            tb1.Rows.Add(rw);
            InternalDisplayMasks validFlag  =   InternalDisplayMasks.None|InternalDisplayMasks.ObjectTypeCode|
                                        InternalDisplayMasks.PrimaryName|InternalDisplayMasks.RequiredForGrid|
                                    InternalDisplayMasks.RequiredForGrid |InternalDisplayMasks.RequiredForForm|
                                    InternalDisplayMasks.ValidForForm|InternalDisplayMasks.ValidForAdvancedFind;
            InsertAttributesInTable(tb2, validFlag,includeAttributeOf,filterNullDisplayName,filterValidForRead,filterValidForCreate,filterValidForUpdate);
            return ds;
        }

        /// <summary>
        /// Insert record in a new DataTable  with a schema that conforms to CreateTable
        /// </summary>
        /// <param name="DataTable">DataTable to insert values in</param>
        /// <returns>same DatatTable</returns>
        public DataTable InsertAttributesInTable(DataTable tb,InternalDisplayMasks Mask,bool includeAttributeOf,bool filterNullDisplayName, bool filterValidForRead,bool filterValidForCreate,bool filterValidForUpdate)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable includeAttributeOf: {0}, filterNullDisplayName: {1}, filterValidForRead: {2}, filterValidForCreate: {3}, filterValidForUpdate: {4}",
                    new object[]{includeAttributeOf,filterNullDisplayName,filterValidForRead,filterValidForCreate,filterValidForUpdate}),CRMQueryTraceSwitch.Sw.Info);
            // Add every field in arraylist as a new row
            int cnt                                 =   AttributeCount;
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable AttributeCount :{0}", cnt),CRMQueryTraceSwitch.Sw.Info);
            //Cannot specify child attributes in the columnset for Retrieve
            for (int i = 0; i < cnt; i++)
            {
                string Name                         =   AttFieldAsString(i,InternalAttributeFields.Name);
                string DisplayName                  =   AttFieldAsString(i,InternalAttributeFields.DisplayName);
                string Description                  =   AttFieldAsString(i,InternalAttributeFields.Description);
                string DefaultValue                 =   AttFieldAsString(i,InternalAttributeFields.DefaultValue);
                string AttributeOf                  =   AttFieldAsString(i,InternalAttributeFields.AttributeOf);

                int attIntType                      =   AttFieldAsInt(i,InternalAttributeFields.Type);
                string attType                      =   AttFieldAsString(i,InternalAttributeFields.Type);
                InternalDisplayMasks DisplayMask    =   (InternalDisplayMasks)AttFieldAsInt(i,InternalAttributeFields.DisplayMask);
                int RequiredLevel                   =   AttFieldAsInt(i,InternalAttributeFields.RequiredLevel);
                bool IsCustomField                  =   AttFieldAsBool(i,InternalAttributeFields.IsCustomField);
                bool ValidForRead                   =   AttFieldAsBool(i,InternalAttributeFields.ValidForRead);
                bool ValidForCreate                 =   AttFieldAsBool(i,InternalAttributeFields.ValidForCreate);
                bool ValidForUpdate                 =   AttFieldAsBool(i,InternalAttributeFields.ValidForUpdate);
                if ( CRMMetaTrace.Sw.TraceVerbose )
                {
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Name: {0}, DisplayMask: {1}, Type: {2}", Name,DisplayMask,attType),CRMQueryTraceSwitch.Sw.Info);
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable SchemaName:{0}", AttFieldAsString(i,InternalAttributeFields.SchemaName)),CRMQueryTraceSwitch.Sw.Info);
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable AggregateOf:{0}", AttFieldAsString(i,InternalAttributeFields.AggregateOf)),CRMQueryTraceSwitch.Sw.Info);
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable AttributeOf:{0}", AttributeOf),CRMQueryTraceSwitch.Sw.Info);
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable CalculationOf:{0}", AttFieldAsString(i,InternalAttributeFields.CalculationOf)),CRMQueryTraceSwitch.Sw.Info);
                //Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MetadataId:{0}", AttFieldAsString(i,InternalAttributeFields.MetadataId)),CRMQueryTraceSwitch.Sw.Info);
                //Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]YomiOf:{0}", AttFieldAsString(i,InternalAttributeFields.YomiOf)),CRMQueryTraceSwitch.Sw.Info);
                }
                // key attributes like accountid generate automatic properties for 'virtual' attributes like accountidname,accountiddsc (dsc not always ? when ?)
                // picklist attributes like xxcode generate... xxxcodename,
                // boolean attributes like donotemail generate...donotemailname
                // lookup like parentcustomerid generate...parentcustomeridtype
                // masterid generate something like masteridcontactname ???
                // systemuserid something like systemuseridyominame....
                if (!includeAttributeOf && !string.IsNullOrEmpty(AttributeOf))
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable avoided Name: {0} attribute of: {1}", Name,AttributeOf),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }

                bool check                          =   wrapper.Util.CheckBit(Mask,DisplayMask);
                
                if ( !check )
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Check false for: {0}, DisplayMask: {1}", Name,DisplayMask),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if ( filterNullDisplayName && string.IsNullOrEmpty(DisplayName))
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable att.DisplayName null for: {0}", Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if (filterValidForRead && !ValidForRead)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForRead for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if (filterValidForCreate && !ValidForCreate)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForCreate for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if ( filterValidForUpdate && !ValidForUpdate)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForUpdate for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                DataRow rw                              =   tb.NewRow();

                if ( tb.Columns.Contains(CrmStr.strConfTypeCode) )
                    rw[CrmStr.strConfTypeCode]         =   ObjectTypeCode;
                if ( tb.Columns.Contains(CrmStr.strConfSchemaName) )
                    rw[CrmStr.strConfSchemaName]       =   Name;
                if ( tb.Columns.Contains(CrmStr.strConfDisplayName) )
                    rw[CrmStr.strConfDisplayName]      =   (string.IsNullOrEmpty(DisplayName)) ? Name:DisplayName;
                if ( tb.Columns.Contains(CrmStr.strConfDisplayMask) )
                    rw[CrmStr.strConfDisplayMask]      =   (int)DisplayMask;
                if ( tb.Columns.Contains(CrmStr.strConfLabelName) )
                    rw[CrmStr.strConfLabelName]        =   DisplayName;
                if ( tb.Columns.Contains(CrmStr.strConfRequiredLevel) )
                    rw[CrmStr.strConfRequiredLevel]    =  RequiredLevel;
                if ( tb.Columns.Contains(CrmStr.strConfIntType) )
                    rw[CrmStr.strConfIntType]          =   attIntType;
                if ( tb.Columns.Contains(CrmStr.strConfType) )
                    rw[CrmStr.strConfType]             =   attType;
                if ( tb.Columns.Contains(CrmStr.strConfDescription) )
                    rw[CrmStr.strConfDescription]      =   Description;
                if ( tb.Columns.Contains(CrmStr.strConfIsCustomField) )
                    rw[CrmStr.strConfIsCustomField]    =   IsCustomField;
                if ( tb.Columns.Contains(CrmStr.strConfDefaultValue) )
                    rw[CrmStr.strConfDefaultValue]     =   DefaultValue;// this will be the string value of the object
                if ( tb.Columns.Contains(CrmStr.strConfValidForCreate) )
                    rw[CrmStr.strConfValidForCreate]   =  ValidForCreate;
                if ( tb.Columns.Contains(CrmStr.strConfValidForRead) )
                    rw[CrmStr.strConfValidForRead]     =   ValidForRead;
                if ( tb.Columns.Contains(CrmStr.strConfValidForUpdate) )
                    rw[CrmStr.strConfValidForUpdate]   =   ValidForUpdate;
                if ( tb.Columns.Contains(CrmStr.strConfAttributeOf) )
                    rw[CrmStr.strConfAttributeOf]      =   AttributeOf;
                tb.Rows.Add(rw);
            }
            return tb;
        }

        /// <summary>
        /// Insert record in a new DataTable  with a schema that conforms to CreateTable
        /// </summary>
        /// <param name="DataTable">DataTable to insert values in</param>
        /// <returns>same DatatTable</returns>
        public DataTable InsertAttributesInTable(DataTable tb,InternalDisplayMasks Mask,bool filterValidForRead,bool filterValidForCreate,bool filterValidForUpdate,AttributeInternalType tp)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable filterValidForRead: {0}, filterValidForCreate: {1}, filterValidForUpdate: {2}, AttributeType: {3}",
                    new object[]{filterValidForRead,filterValidForCreate,filterValidForUpdate,tp}),CRMQueryTraceSwitch.Sw.Info);
            // Add every field in arraylist as a new row
            int cnt                                 =   AttributeCount;
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable AttributeCount :{0}", cnt),CRMQueryTraceSwitch.Sw.Info);
            //Cannot specify child attributes in the columnset for Retrieve
            for (int i = 0; i < cnt; i++)
            {
                string Name                         =   AttFieldAsString(i,InternalAttributeFields.Name);
                string DisplayName                  =   AttFieldAsString(i,InternalAttributeFields.DisplayName);
                string Description                  =   AttFieldAsString(i,InternalAttributeFields.Description);
                string DefaultValue                 =   AttFieldAsString(i,InternalAttributeFields.DefaultValue);
                string AttributeOf                  =   AttFieldAsString(i,InternalAttributeFields.AttributeOf);

                AttributeInternalType attIntType    =   (AttributeInternalType)AttFieldAsInt(i,InternalAttributeFields.AttributeType);
                string attType                      =   AttFieldAsString(i,InternalAttributeFields.Type);
                if ( ( attIntType != tp ) || !string.IsNullOrEmpty(AttributeOf) || string.IsNullOrEmpty(DisplayName) )
                    continue;
                InternalDisplayMasks DisplayMask    =   (InternalDisplayMasks)AttFieldAsInt(i,InternalAttributeFields.DisplayMask);
                bool check                          =   wrapper.Util.CheckBit(Mask,DisplayMask);
                if ( !check )
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Check false for: {0}, DisplayMask: {1}", Name,DisplayMask),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                int RequiredLevel                   =   AttFieldAsInt(i,InternalAttributeFields.RequiredLevel);
                bool IsCustomField                  =   AttFieldAsBool(i,InternalAttributeFields.IsCustomField);
                bool ValidForRead                   =   AttFieldAsBool(i,InternalAttributeFields.ValidForRead);
                bool ValidForCreate                 =   AttFieldAsBool(i,InternalAttributeFields.ValidForCreate);
                bool ValidForUpdate                 =   AttFieldAsBool(i,InternalAttributeFields.ValidForUpdate);
                if ( CRMMetaTrace.Sw.TraceVerbose )
                {
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Name: {0}, DisplayMask: {1}, Type: {2}", Name,DisplayMask,attType),CRMQueryTraceSwitch.Sw.Info);
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable SchemaName:{0}", AttFieldAsString(i,InternalAttributeFields.SchemaName)),CRMQueryTraceSwitch.Sw.Info);
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable AggregateOf:{0}", AttFieldAsString(i,InternalAttributeFields.AggregateOf)),CRMQueryTraceSwitch.Sw.Info);
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable AttributeOf:{0}", AttributeOf),CRMQueryTraceSwitch.Sw.Info);
                }

                if (filterValidForRead && !ValidForRead)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForRead for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if (filterValidForCreate && !ValidForCreate)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForCreate for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if ( filterValidForUpdate && !ValidForUpdate)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForUpdate for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                DataRow rw                          =   tb.NewRow();

                if ( tb.Columns.Contains(CrmStr.strConfTypeCode) )
                    rw[CrmStr.strConfTypeCode]      =   ObjectTypeCode;
                if ( tb.Columns.Contains(CrmStr.strConfSchemaName) )
                    rw[CrmStr.strConfSchemaName]    =   Name;
                if ( tb.Columns.Contains(CrmStr.strConfDisplayName) )
                    rw[CrmStr.strConfDisplayName]   =   (string.IsNullOrEmpty(DisplayName)) ? Name:DisplayName;
                if ( tb.Columns.Contains(CrmStr.strConfDisplayMask) )
                    rw[CrmStr.strConfDisplayMask]   =   (int)DisplayMask;
                if ( tb.Columns.Contains(CrmStr.strConfLabelName) )
                    rw[CrmStr.strConfLabelName]     =   DisplayName;
                if ( tb.Columns.Contains(CrmStr.strConfRequiredLevel) )
                    rw[CrmStr.strConfRequiredLevel] =   RequiredLevel;
                if ( tb.Columns.Contains(CrmStr.strConfType) )
                    rw[CrmStr.strConfType]          =   attType;
                if ( tb.Columns.Contains(CrmStr.strConfIntType) )
                    rw[CrmStr.strConfIntType]       =   attIntType;
                if ( tb.Columns.Contains(CrmStr.strConfDescription) )
                    rw[CrmStr.strConfDescription]   =   Description;
                if ( tb.Columns.Contains(CrmStr.strConfIsCustomField) )
                    rw[CrmStr.strConfIsCustomField] =   IsCustomField;
                if ( tb.Columns.Contains(CrmStr.strConfDefaultValue) )
                    rw[CrmStr.strConfDefaultValue]  =   DefaultValue;// this will be the string value of the object
                if ( tb.Columns.Contains(CrmStr.strConfValidForCreate) )
                    rw[CrmStr.strConfValidForCreate]=   ValidForCreate;
                if ( tb.Columns.Contains(CrmStr.strConfValidForRead) )
                    rw[CrmStr.strConfValidForRead]  =   ValidForRead;
                if ( tb.Columns.Contains(CrmStr.strConfValidForUpdate) )
                    rw[CrmStr.strConfValidForUpdate]=   ValidForUpdate;
                tb.Rows.Add(rw);
            }
            return tb;
        }

        /// <summary>
        /// Build a string with attributes schema names corresponding to flags sperataed by';'
        /// </summary>
        public string ExtractAttributesByFlag(InternalDisplayMasks Mask,bool filterValidForRead,bool filterValidForCreate,bool filterValidForUpdate)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig ExtractAttributesByFlag Mask: {0}, filterValidForRead: {1}, filterValidForCreate: {2}, filterValidForUpdate: {3}",new object[]{Mask,filterValidForRead,filterValidForCreate,filterValidForUpdate}),CRMQueryTraceSwitch.Sw.Info);
            StringBuilder sb                        =   new StringBuilder();
            int cnt                                 =   AttributeCount;
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig ExtractAttributesByFlag AttributeCount :{0}", cnt),CRMQueryTraceSwitch.Sw.Info);
            for (int i = 0; i < cnt; i++)
            {
                InternalDisplayMasks DisplayMask    =   (InternalDisplayMasks)AttFieldAsInt(i,InternalAttributeFields.DisplayMask);
                string Name                         =   AttFieldAsString(i,InternalAttributeFields.Name);
                string DisplayName                  =   AttFieldAsString(i,InternalAttributeFields.DisplayName);
                bool ValidForRead                   =   AttFieldAsBool(i,InternalAttributeFields.ValidForRead);
                bool ValidForCreate                 =   AttFieldAsBool(i,InternalAttributeFields.ValidForCreate);
                bool ValidForUpdate                 =   AttFieldAsBool(i,InternalAttributeFields.ValidForUpdate);

                bool check                          =   wrapper.Util.CheckBit(Mask,DisplayMask);
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine(
                        string.Format("[V]EntityConfig ExtractAttributesByFlag attribute i=[{0}], name:[{1}], check [{2}], displayName=[{3}], displayMask=[{4}]",
                        new Object[] { i, Name, check, DisplayName, DisplayMask}),CRMQueryTraceSwitch.Sw.Info);
                
                if ( !check || string.IsNullOrEmpty(DisplayName) )
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Flags not Ok for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if (filterValidForRead && !ValidForRead)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForRead for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if (filterValidForCreate && !ValidForCreate)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForCreate for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                if ( filterValidForUpdate && !ValidForUpdate)
                {
                    if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig InsertAttributesInTable Not ValidForUpdate for: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                    continue;
                }
                sb.AppendFormat("{0};",Name);
            }
            string strRet   =   sb.ToString();
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig ExtractAttributesByFlag strRet: {0}",strRet),CRMQueryTraceSwitch.Sw.Info);
            return strRet;
        }

        /// <summary>
        /// Create a new DataTable ready to receive content from a retrieve request
        /// </summary>
        /// <param name="fieldList">Arraylist being used to render table</param>
        /// <returns>HTML of table</returns>
        public DataTable CreateContentTable(string[] colArr)
        {
            DataTable tb = new DataTable(entityName);
            return CreateContentTable(tb,colArr);
        }

        /// <summary>
        /// Create a DataTable with all columns from the Crm entity
        /// </summary>
        public DataTable CreateContentTable(DataTable tb)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig CreateContentTable BEG attributesDic.Keys.Count: {0}",  
                    attributesDic.Keys.Count),CRMQueryTraceSwitch.Sw.Info);
            // we take only selected values
            foreach (string key in attributesDic.Keys)
            {
                AddColumn(tb,key);
            }
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig CreateContentTable END Count: {0}",tb.Columns.Count),CRMQueryTraceSwitch.Sw.Info);
            return tb;
        }

        /// <summary>
        /// Create a DataTable with columns from the Query or all Crm entity attributes
        /// </summary>
        public DataTable CreateContentTable(DataTable tb,string[] colArr)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig CreateContentTable BEG colArr == null: {0}, attributesDic.Keys.Count: {1}",  
                    colArr==null ,attributesDic.Keys.Count),CRMQueryTraceSwitch.Sw.Info);
            if ( colArr == null || colArr.Length == 0 )
                return CreateContentTable(tb);
            for (int i = 0; i < colArr.Length; i++)
            {
                bool found      =   false;
                foreach (string key in attributesDic.Keys)
                {
                    string Name =   AttFieldAsString(key, InternalAttributeFields.Name);
                    if (colArr[i] != Name)
                        continue;
                    if ( AddColumn(tb,key) != null )
                        found       =   true;
                }
                if (CRMQueryTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig CreateContentTable col: {0}, found: {1}",colArr[i],found), CRMQueryTraceSwitch.Sw.Info);
            }
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig CreateContentTable END Count: {0}",tb.Columns.Count),CRMQueryTraceSwitch.Sw.Info);
            return tb;
        }

        /// <summary>
        /// Add one or more new columns in a datatable depending on the attribute type
        /// Numeric types add a sup name__formatted column 
        /// DataTime types add a sup name__date column and a name__time column
        /// Lookup, Owner, Customer types add a sup name__typecode column and a name__value column
        /// PickList, StusCode types add a sup name__typecode column 
        /// </summary>
        public DataColumn AddColumn(DataTable tb, string key)
        {
            string Name                         =   AttFieldAsString(key,InternalAttributeFields.Name);
            string DisplayName                  =   AttFieldAsString(key,InternalAttributeFields.DisplayName);
            string attType                      =   AttFieldAsString(key,InternalAttributeFields.Type);
            int attTypeInt                      =   AttFieldAsInt(key,InternalAttributeFields.Type);
            InternalDisplayMasks DisplayMask    =   (InternalDisplayMasks)AttFieldAsInt(key,InternalAttributeFields.DisplayMask);
            bool ValidForRead                   =   AttFieldAsBool(key,InternalAttributeFields.ValidForRead);
            AttributeInternalType atttp         =   (AttributeInternalType )attTypeInt;
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig AddColumn BEG name: {0}, displayName: {1}, Type: {2}, IType: {3}, ValidForRead: {4}, DisplayMask: {5}",
                    new object[]{Name,DisplayName,attType,atttp,ValidForRead,DisplayMask} ),CRMQueryTraceSwitch.Sw.Info);
            if ( ( (DisplayMask & InternalDisplayMasks.ObjectTypeCode) == InternalDisplayMasks.ObjectTypeCode ) || 
                ((Name == CrmStr.objecttypecode && atttp == AttributeInternalType.Picklist)) )
            {
                AttributeInternalType oldatttp  =   atttp;
                atttp                           =   AttributeInternalType.EntityNameReference;
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig AddColumn changing type for Key: {0}, name: {1}, from : {2}, to : {3}",
                        new object[]{key, Name,oldatttp,atttp}),CRMQueryTraceSwitch.Sw.Info);
            }
            if ( !ValidForRead )
            {
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine(
                    string.Format("[V]EntityConfig AddColumn invalid for read : {0}, name: {1}",attType, Name),CRMQueryTraceSwitch.Sw.Info);
                return null;
            }
            DataColumn c                        =   null;
            switch (atttp)
            {
                case AttributeInternalType.Float:
                    tb.Columns.Add(Name+CrmStr.InternalRowFormat, typeof(string));
                    c                           =   tb.Columns.Add(Name, typeof(float));
                    break;
                case AttributeInternalType.Integer:
                    tb.Columns.Add(Name+CrmStr.InternalRowFormat, typeof(string));
                    c                           =   tb.Columns.Add(Name, typeof(int));
                    break;
                case AttributeInternalType.Status:
                case AttributeInternalType.Picklist:
                    tb.Columns.Add(Name+CrmStr.InternalRowTypecode, typeof(string));
                    c                           =   tb.Columns.Add(Name, typeof(int));
                    break;
                case AttributeInternalType.Decimal:
                    tb.Columns.Add(Name+CrmStr.InternalRowFormat, typeof(string));
                    c                           =   tb.Columns.Add(Name, typeof(decimal));
                    break;
                case AttributeInternalType.Virtual:// virtual added by crm for exemple picklist option name or boolean->(iskitname)
                case AttributeInternalType.EntityNameReference:
                case AttributeInternalType.State:
                case AttributeInternalType.String:
                case AttributeInternalType.Memo:
                    c                           =   tb.Columns.Add(Name, typeof(string));
                    break;
                case AttributeInternalType.DateTime:
                    tb.Columns.Add(Name+CrmStr.InternalRowDate, typeof(string));
                    tb.Columns.Add(Name+CrmStr.InternalRowTime, typeof(string));
                    c                           =   tb.Columns.Add(Name, typeof(DateTime));// ATTN
                    break;
                case AttributeInternalType.Money:
                    tb.Columns.Add(Name+CrmStr.InternalRowFormat, typeof(string));
                    c                           =   tb.Columns.Add(Name, typeof(decimal));// ATTN
                    break;
                case AttributeInternalType.Boolean:
                    c                           =   tb.Columns.Add(Name, typeof(bool));
                    break;
                case AttributeInternalType.PrimaryKey:
                case AttributeInternalType.UniqueIdentifier:
                    c                           =   tb.Columns.Add(Name, typeof(Guid));
                    break;
                case AttributeInternalType.Customer:
                case AttributeInternalType.Owner:
                case AttributeInternalType.Lookup:
                    tb.Columns.Add(Name+CrmStr.InternalRowTypecode, typeof(string));
                    tb.Columns.Add(Name+CrmStr.InternalRowVal, typeof(string));
                    c                           =   tb.Columns.Add(Name, typeof(Guid));
                    break;
                default:
                    if ( CRMQueryTraceSwitch.Sw.TraceError )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]EntityConfig AddColumn missing type : {0}, name: {1}",attType, Name),CRMQueryTraceSwitch.Sw.Info);
                    return null;
            }
            if (c == null)
            {
                if ( CRMQueryTraceSwitch.Sw.TraceError)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]EntityConfig AddColumn COLUMN NOT added to table : {0}", Name),CRMQueryTraceSwitch.Sw.Info);
                return null;
            }
            // Add column 's caption
            if ( !string.IsNullOrEmpty(DisplayName) ) 
                c.Caption                       =   DisplayName;
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig AddColumn column added : {0}",Name),CRMQueryTraceSwitch.Sw.Info);
            if ( tb.PrimaryKey == null && ((DisplayMask & InternalDisplayMasks.PrimaryName) == InternalDisplayMasks.PrimaryName))
            {
                if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig AddColumn PrimaryKey with: {0}",Name),CRMQueryTraceSwitch.Sw.Info);
                tb.PrimaryKey                   =   new DataColumn[] { c };
            }
            return c;
        }

        /// <summary>
        /// Check to see if any translation is necessary for all columns in the row sent as parameter
        /// </summary>
        public void LocalizeColumns(DynEntity dyn,DataRow rw,int LCID)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig LocalizeColums BEG attributesDic.Keys.Count: {0}",  
                    attributesDic.Keys.Count),CRMQueryTraceSwitch.Sw.Info);
            // we take only selected values
            foreach (string key in attributesDic.Keys)
            {
                LocalizeColumn(dyn,rw,key,LCID);
            }
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( "[V]EntityConfig LocalizeColums END",CRMQueryTraceSwitch.Sw.Info);  
        }

        /// <summary>
        /// Check to see if any translation is necessary for columns in teh row sent as parameter
        /// </summary>
        public void LocalizeColumns(DynEntity dyn,DataRow rw,string[] colArr,int LCID)
        {
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig LocalizeColumns BEG colArr == null: {0}, attributesDic.Keys.Count: {1}",  
                    colArr==null ,attributesDic.Keys.Count),CRMQueryTraceSwitch.Sw.Info);
            if ( colArr == null || colArr.Length == 0 )
            {
                LocalizeColumns(dyn,rw,LCID);
                return;
            }
            for (int i = 0; i < colArr.Length; i++)
            {
                bool found      =   false;
                foreach (string key in attributesDic.Keys)
                {
                    string Name =   AttFieldAsString(key, InternalAttributeFields.Name);
                    if (colArr[i] != Name)
                        continue;
                    found       =   LocalizeColumn(dyn,rw,key,LCID);
                }
                if (CRMQueryTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig LocalizeColumns col: {0}, found: {1}",colArr[i],found), CRMQueryTraceSwitch.Sw.Info);
            }
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( "[V]EntityConfig LocalizeColumns END",CRMQueryTraceSwitch.Sw.Info);  
        }

        public bool LocalizeColumn(DynEntity dyn, DataRow rw,string key,int LCID)
        {
            int attTypeInt              =   AttFieldAsInt(key,InternalAttributeFields.Type);
            AttributeInternalType atttp =   (AttributeInternalType )attTypeInt;
            if ( CRMQueryTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig LocalizeColumn BEG key: {0}, Type: {1}, LCID: {2}",key,atttp,LCID ),CRMQueryTraceSwitch.Sw.Info);
            switch (atttp)
            {
                default:
                    return false;
                case AttributeInternalType.String:
                case AttributeInternalType.Memo:
                    break;
            }
            DynEntityLocalizeArgs e     =   new DynEntityLocalizeArgs(dyn,key,LCID);
            dyn.OnDynEntityEvent(dyn,e);
            if ( CRMDynTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]EntityConfig LocalizeColumn Found: {0}, LocalizedValue: {1}.",e.Found,e.LocalizedValue),CRMDynTraceSwitch.Sw.Info);
            if ( e.Found )
                rw[key]                 =   e.LocalizedValue;
            return e.Found;
        }
    }
}
